<?php

namespace App\Http\Controllers;

use App\Common\Machine\PlayerQueue;
use App\Events\MachineStatusChanged;
use App\Http\Resources\DollRecords;
use App\Http\Resources\GameRecords;
use App\Models\CoinRecord;
use App\Models\GameRecord;
use App\Models\Machine;
use App\Services\Doll;
use App\Support\Time;
use Illuminate\Http\Request;

class GameRecordsController extends Controller
{
    private $doll;

    /**
     * 注入硬件对接服务
     *
     * @param Doll $doll
     */
    public function __construct(Doll $doll)
    {
        $this->doll = $doll;
    }

    /**
     * 游戏记录列表
     *
     * @param Request $request
     * @return GameRecords
     */
    public function index(Request $request)
    {
        $filter = [];
        if ($request->filled('operate_result')) {
            $filter['operate_result'] = $request->get('operate_result');
        }

        $paginate = $request->user()->gameRecords()->where($filter)->orderByDesc('id')->simplePaginate(per_page());
        return new GameRecords($paginate);
    }

    /**
     * 获取游戏结果
     *
     * @param Request $request
     * @param GameRecord $gameRecord
     * @return array|\Illuminate\Http\JsonResponse
     */
    public function show(Request $request, GameRecord $gameRecord)
    {
        if ($gameRecord->user_id != $request->user()->id) {
            return access_denied();
        }
        
        $data = [
            'id' => $gameRecord->id,
            'operate_result' => $gameRecord->operate_result,
        ];
        
        return $this->wrapData($data);
    }

    /**
     * 开始游戏
     *
     * @param Request $request
     * @return array|\Illuminate\Http\JsonResponse
     */
    public function store(Request $request)
    {
        $user = $request->user();
        $userId = $user->id;

        /** @var \App\Models\Machine $machine */
        $machine = Machine::findOrFail($request->post('machine_id'));
        if (! $machine->enable()) {
            return invalidation('设备维护中');
        }
        
        $coinNeeds = $machine->coin;
        if ($user->coin < $coinNeeds) {
            return ['code' => 1001, 'message' => '游戏币不足，请充值'];
        }
        
        if ($machine->isWorking()) {
            return ['code' => 1002, 'message' => '请预约排队'];
        } else {
            $nextUserId = (new PlayerQueue($machine))->nextUserId();
            if ($nextUserId && $nextUserId != $userId) {
                return ['code' => 1002, 'message' => '请预约排队'];
            }
        }

        try {
            $result = $this->doll->dollAssign($machine->device_id, $userId);
        } catch (\Exception $e) {
            return invalidation($e->getMessage());
        }

        // 生成游戏记录
        $record = GameRecord::create([
            'user_id' => $userId,
            'machine_id' => $machine->id,
            'log_id' => $result['log_id'] ?? 0,
            'coin' => $coinNeeds,
            'ware_id' => 0,
            'ware_name' => $machine->ware_name,
            'ware_image' => $machine->ware_image,
        ]);

        // 生成游戏币记录
        CoinRecord::create([
            'user_id' => $userId,
            'coin' => -$coinNeeds, // 消费游戏币，值为负数
            'recordable_type' => CoinRecord::TYPE_GAME_RECORD,
            'recordable_id' => $record->id,
        ]);

        // 扣除用户的游戏币
        $user->decrement('coin', $coinNeeds);

        // 更新设备状态
        // 此处不能使用 `Illuminate\Database\Eloquent\Model::update()`，因为它会做 dirty check，如果两个值一致会忽略更新
        // 但应用中存在其他进程，可能会并发修改这些值，如 `App\Console\Commands\CheckPlayerQueues.php` 会修改 latest_user_id
        // 使用 `Illuminate\Database\Eloquent\Builder::update()` 进行更新
        Machine::where('id', $machine->id)->update([
            'device_status' => Machine::DEVICE_STATUS_WORKING,
            'game_record_id' => $record->id,
            'latest_user_id' => $userId,
            'played_at' => Time::now(),
        ]);

        // 清除“游戏机开始权限”的占用标记
        $playerQueue = new PlayerQueue($machine);
        $playerQueue->clearOccupancyMark();
        
        // 广播事件：游戏机状态改变
        event(new MachineStatusChanged($machine));
        
        $data = [
            'id' => $record->id,
            'assign_info' => $result
        ];

        return [
            'code' => 0,    // 业务码
            'message' => 'ok',
            'data' => $data,
        ];
    }

    /**
     * 游戏机硬件系统游戏结果回调
     *
     * @param \Illuminate\Http\Request $request
     * @return array|\Illuminate\Http\JsonResponse
     */
    public function update(Request $request)
    {
        do {
            $params = $request->all();
            if (!$this->doll->checkSign($params['sign'] ?? '', $params)) {
                $error = '验证失败';
                break;
            }

            $logId = $params['log_id'];
            $operateResult = $params['operate_result'];
            if (!$logId || !$operateResult) {
                $error = '参数有误';
                break;
            }

            if (! $record = GameRecord::where('log_id', $logId)->first()) {
                $error = '记录不存在';
                break;
            }

            if ($record->operate_result != GameRecord::OPERATE_ING) {
                return ['message' => '已更新过'];
            }
            
            // 更新记录
            $record->update([
                'operate_result' => $operateResult == 1 ? GameRecord::OPERATE_SUCCESS : GameRecord::OPERATE_FAILED,
                'ware_status' => $operateResult == 1 ? GameRecord::WARE_STATUS_RECEIVABLE : GameRecord::WARE_STATUS_NONE,
                'ware_id' => $params['goods_id'] ?? 0,
                'ended_at' => Time::now(),
            ]);

            // 该游戏结果为最近一次游戏记录的结果时
            if ($record->id == $record->machine->game_record_id) {
                // 复位
                $machine = $record->machine;
                $machine->update(['device_status' => Machine::DEVICE_STATUS_IDLE]);
                
                // 清除“游戏机开始权限”的占用标记
                $playerQueue = new PlayerQueue($machine);
                $playerQueue->clearOccupancyMark();
                
                // 广播事件：游戏机状态改变
                event(new MachineStatusChanged($machine, $record));
            }

            return ['message' => '回调成功'];
        } while (false);
        
        // 记录错误
        log_info('game/callback.error.log', $error . '|' . json_encode($params));
        return invalidation($error);
    }

    /**
     * 游戏控制操作
     *
     * @param Request $request
     *
     * @throws \Exception
     */
    public function dollOperate(Request $request)
    {
        $machine = Machine::find($request->machine_id);
        if ($machine) {
            $this->doll->dollOperate($machine->device_id, $request->user()->id, $request->action);
        }
    }

    /**
     * 我的娃娃记录
     *
     * @param Request $request
     * @return DollRecords
     */
    public function dollRecords(Request $request)
    {
        $user = $request->user();

        $paginate = $user->gameRecords()->where([
            'operate_result' => GameRecord::OPERATE_SUCCESS
        ])->orderByDesc('id')->simplePaginate(per_page());
        
        // 待领取数量
        $count = $user->gameRecords()->where([
            'operate_result' => GameRecord::OPERATE_SUCCESS,
            'ware_status' => GameRecord::WARE_STATUS_RECEIVABLE
        ])->count();
        
        return (new DollRecords($paginate))->additional([
            'meta' => ['receivable_num' => $count]
        ]);
    }
}
